/**
 * Diagram Wrapper组件
 * */

import * as go from 'gojs/release/go';
import {ReactDiagram} from 'gojs-react/lib/types';
import * as React from 'react';


export default class DiagramWrapper1 extends React.Component {

  constructor(props) {
    super(props);
    this.diagramRef = React.createRef();
  }

  /**
   * Get the diagram reference and add any desired diagram listeners.
   * Typically the same function will be used for each listener,
   * with the function using a switch statement to handle the events.
   * This is only necessary when you want to define additional app-specific diagram listeners.
   */
  componentDidMount() {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.addDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    }
    console.log(this.diagramRef)
  }

  /**
   * Get the diagram reference and remove listeners that were added during mounting.
   * This is only necessary when you have defined additional app-specific diagram listeners.
   */
  componentWillUnmount() {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.removeDiagramListener('ChangedSelection', this.props.onDiagramEvent);
    }
  }


  /**
   * Diagram initialization method, which is passed to the ReactDiagram component.
   * This method is responsible for making the diagram and initializing the model, any templates,
   * and maybe doing other initialization tasks like customizing tools.
   * The model's data should not be set here, as the ReactDiagram component handles that via the other props.
   */
  initDiagram = () => {
    const $ = go.GraphObject.make;
    const diagram =
      $(go.Diagram,
        {
          "undoManager.isEnabled": true,
          'clickCreatingTool.archetypeNodeData': {text: 'new node', color: 'lightblue'},
          model: $(go.GraphLinksModel,
            {
              linkKeyProperty: 'key',
              linkFromPortIdProperty: "fromPort",
              linkToPortIdProperty: "toPort",

              // positive keys for nodes
              makeUniqueKeyFunction: (m, data) => {
                let k = data.key || 1;
                while (m.findNodeDataForKey(k)) k++;
                data.key = k;
                return k;
              },
              // negative keys for links
              makeUniqueLinkKeyFunction: (m, data) => {
                let k = data.key || -1;
                while (m.findLinkDataForKey(k)) k--;
                data.key = k;
                return k;
              },


            })
        });

    // 添加button
    function makeButton(text, action, visiblePredicate) {
      return $("ContextMenuButton",
        $(go.TextBlock, text),
        {click: action},

        visiblePredicate ? new go.Binding("visible", "", function (o, e) {
          return o.diagram ? visiblePredicate(o, e) : false;
        }).ofObject() : {})
    }


    function changeColor(port) {
      diagram.startTransaction("colorPort");
      let data = port.data;
      diagram.model.setDataProperty(data, "portColor", getPortColor());
      diagram.commitTransaction("colorPort");
    }

    function getPortColor() {
      let portColors = ["#fae3d7", "#d6effc", "#ebe3fc", "#eaeef8", "#fadfe5", "#6cafdb", "#66d6d1"]
      return portColors[Math.floor(Math.random() * portColors.length)];
    }

    // 添加连线点
    function addPort(side) {
      diagram.startTransaction("addPort");
      diagram.selection.each(function (node) {
        // skip any selected Links
        if (!(node instanceof go.Node)) return;
        // compute the next available index number for the side
        let i = 0;
        while (node.findPort(side + i.toString()) !== node) i++;
        // now this new port name is unique within the whole Node because of the side prefix
        let name = side + i.toString();
        // get the Array of port data to be modified
        let arr = node.data[side + "Array"];
        if (arr) {
          var newportdata = {
            portId: name,
            portColor: getPortColor()
          };
          diagram.model.insertArrayItem(arr, -1, newportdata);
        }
      });
      diagram.commitTransaction("addPort");
    }


    function removePort(port) {
      diagram.startTransaction("removePort");
      let pid = port.portId;
      let arr = port.panel.itemArray;
      for (let i = 0; i < arr.length; i++) {
        if (arr[i].portId === pid) {
          diagram.model.removeArrayItem(arr, i);
          break;
        }
      }
      diagram.commitTransaction("removePort");
    }

    function removeAll(port) {
      diagram.startTransaction("removePorts");
      let nodedata = port.part.data;
      let side = port._side;  // there are four property names, all ending in "Array"
      diagram.model.setDataProperty(nodedata, side + "Array", []);  // an empty Array
      diagram.commitTransaction("removePorts");
    }

    function CustomLink() {
      go.Link.call(this);
    }

    go.Diagram.inherit(CustomLink, go.Link);
    CustomLink.prototype.findSidePortIndexAndCount = function (node, port) {
      let nodedata = node.data;
      if (nodedata !== null) {
        let portdata = port.data;
        let side = port._side;
        let arr = nodedata[side + "Array"];
        let len = arr.length;
        for (let i = 0; i < len; i++) {
          if (arr[i] === portdata) return [i, len];
        }
      }
      console.log('---------------------', nodedata)
      return [-1, 1];
    };
    CustomLink.prototype.computeEndSegmentLength = function (node, port, spot, from) {
      let esl = go.Link.prototype.computeEndSegmentLength.call(this, node, port, spot, from);
      let other = this.getOtherPort(port);
      if (port !== null && other !== null) {
        let thispt = port.getDocumentPoint(this.computeSpot(from));
        let otherpt = other.getDocumentPoint(this.computeSpot(!from));
        if (Math.abs(thispt.x - otherpt.x) > 20 || Math.abs(thispt.y - otherpt.y) > 20) {
          let info = this.findSidePortIndexAndCount(node, port);
          let idx = info[0];
          let count = info[1];
          if (port._side === "top" || port._side === "bottom") {
            if (otherpt.x < thispt.x) {
              return esl + 4 + idx * 8;
            } else {
              return esl + (count - idx - 1) * 8;
            }
          } else {  // left or right
            if (otherpt.y < thispt.y) {
              return esl + 4 + idx * 8;
            } else {
              return esl + (count - idx - 1) * 8;
            }
          }
        }
      }
      return esl;
    };
    CustomLink.prototype.hasCurviness = function () {
      if (isNaN(this.curviness)) return true;
      return go.Link.prototype.hasCurviness.call(this);
    };
    CustomLink.prototype.computeCurviness = function () {
      if (isNaN(this.curviness)) {
        let fromnode = this.fromNode;
        let fromport = this.fromPort;
        let fromspot = this.computeSpot(true);
        let frompt = fromport.getDocumentPoint(fromspot);
        let tonode = this.toNode;
        let toport = this.toPort;
        let tospot = this.computeSpot(false);
        let topt = toport.getDocumentPoint(tospot);
        if (Math.abs(frompt.x - topt.x) > 20 || Math.abs(frompt.y - topt.y) > 20) {
          if ((fromspot.equals(go.Spot.Left) || fromspot.equals(go.Spot.Right)) &&
            (tospot.equals(go.Spot.Left) || tospot.equals(go.Spot.Right))) {
            let fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
            let toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
            let c = (fromseglen - toseglen) / 2;
            if (frompt.x + fromseglen >= topt.x - toseglen) {
              if (frompt.y < topt.y) return c;
              if (frompt.y > topt.y) return -c;
            }
          } else if ((fromspot.equals(go.Spot.Top) || fromspot.equals(go.Spot.Bottom)) &&
            (tospot.equals(go.Spot.Top) || tospot.equals(go.Spot.Bottom))) {
            let fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
            let toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
            let c = (fromseglen - toseglen) / 2;
            if (frompt.x + fromseglen >= topt.x - toseglen) {
              if (frompt.y < topt.y) return c;
              if (frompt.y > topt.y) return -c;
            }
          }
        }
      }
      return go.Link.prototype.computeCurviness.call(this);
    };

    const nodeMenu =
      $("ContextMenu",
        makeButton("复制",
          function (e, obj) {
            e.diagram.commandHandler.copySelection();
          }),
        makeButton("删除",
          function (e, obj) {
            e.diagram.commandHandler.deleteSelection();
          }),
        $(go.Shape, "LineH", {strokeWidth: 2, height: 1, stretch: go.GraphObject.Horizontal}),
        makeButton("顶部添加连接点",
          function (e, obj) {
            console.log("1111111111", e, obj)
            addPort("top");
          }),
        makeButton("左侧添加连接点",
          function (e, obj) {
            addPort("left");
          }),
        makeButton("右侧添加连接点",
          function (e, obj) {
            addPort("right");
          }),
        makeButton("底部添加连接点",
          function (e, obj) {
            addPort("bottom");
          })
      );

    const portSize = new go.Size(8, 8);
    const portMenu = $("ContextMenu",
      makeButton("移除连接点",
        function (e, obj) {
          removePort(obj.part.adornedObject);
        }),
      makeButton("连接点颜色",
        function (e, obj) {
          changeColor(obj.part.adornedObject);
        }),
      makeButton("删侧连接点",
        function (e, obj) {
          removeAll(obj.part.adornedObject);
        })
    );

    diagram.nodeTemplate =
      $(go.Node, "Table",
        {
          locationObjectName: "BODY",
          locationSpot: go.Spot.Center,
          selectionObjectName: "BODY",
          contextMenu: nodeMenu
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),

        $(go.Panel, "Auto",
          {
            row: 1, column: 1, name: "BODY",
            stretch: go.GraphObject.Fill
          },
          $(go.Shape, "Rectangle",
            {
              fill: "#dbf6cb", stroke: null, strokeWidth: 0,
              minSize: new go.Size(60, 60)
            }),
          $(go.TextBlock,
            {margin: 10, textAlign: "center", font: "bold 14px Segoe UI,sans-serif", stroke: "#484848", editable: true},
            new go.Binding("text", "name").makeTwoWay())
        ),

        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "leftArray"),
          {
            row: 1, column: 0,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "left",  // internal property to make it easier to tell which side it's on
                  fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1, 0)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),

        $(go.Panel, "Horizontal",
          new go.Binding("itemArray", "topArray"),
          {
            row: 0, column: 1,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "top",
                  fromSpot: go.Spot.Top, toSpot: go.Spot.Top,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(0, 1)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Horizontal Panel

        $(go.Panel, "Vertical",
          new go.Binding("itemArray", "rightArray"),
          {
            row: 1, column: 2,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "right",
                  fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(1, 0)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        ),  // end Vertical Panel

        $(go.Panel, "Horizontal",
          new go.Binding("itemArray", "bottomArray"),
          {
            row: 2, column: 1,
            itemTemplate:
              $(go.Panel,
                {
                  _side: "bottom",
                  fromSpot: go.Spot.Bottom, toSpot: go.Spot.Bottom,
                  fromLinkable: true, toLinkable: true, cursor: "pointer",
                  contextMenu: portMenu
                },
                new go.Binding("portId", "portId"),
                $(go.Shape, "Rectangle",
                  {
                    stroke: null, strokeWidth: 0,
                    desiredSize: portSize,
                    margin: new go.Margin(0, 1)
                  },
                  new go.Binding("fill", "portColor"))
              )  // end itemTemplate
          }
        )  // end Horizontal Panel
      );

    diagram.linkTemplate =
      $(CustomLink,
        {
          routing: go.Link.AvoidsNodes,
          corner: 4,
          curve: go.Link.JumpGap,
          reshapable: true,
          resegmentable: true,
        },
        new go.Binding("points").makeTwoWay(),
        $(go.Shape, {stroke: "#2F4F4F", strokeWidth: 2})
      );

    diagram.toolManager.clickCreatingTool.archetypeNodeData = {
      name: "Unit",
      leftArray: [],
      rightArray: [],
      topArray: [],
      bottomArray: []
    };

    diagram.contextMenu =
      $("ContextMenu",
        makeButton("粘贴",
          function (e, obj) {
            e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint);
          },
          function (o) {
            return o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint);
          }),
        makeButton("撤回",
          function (e, obj) {
            e.diagram.commandHandler.undo();
          },
          function (o) {
            return o.diagram.commandHandler.canUndo();
          }),
        makeButton("恢复",
          function (e, obj) {
            e.diagram.commandHandler.redo();
          },
          function (o) {
            return o.diagram.commandHandler.canRedo();
          })
      );
    return diagram;
  };

  render() {
    return (
      <ReactDiagram
        ref={this.diagramRef}
        divClassName='diagram-component'
        initDiagram={this.initDiagram}
        nodeDataArray={this.props.nodeDataArray}
        linkDataArray={this.props.linkDataArray}
        modelData={this.props.modelData}
        onModelChange={this.props.onModelChange}
        skipsDiagramUpdate={this.props.skipsDiagramUpdate}
      />
    );
  }
}